package org.simp.node;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.simp.codec.SimpMessageCodecFactory;
import org.simp.common.Constants;
import org.simp.context.SimpContext;
import org.simp.context.SimpContextWraper;
import org.simp.core.message.SimpMessage;
import org.simp.handler.AbstractSimpHandler;
import org.simp.interceptor.SimpHandlerInterceptor;
import org.simp.router.RouterInterceptor;

public class SimpServerAccepter {

	
	private static final Logger log = Logger.getLogger(SimpServerAccepter.class);
	
	private SimpContext context;
	
	private String ip;
	
	private int port;
	
	private  NioSocketAcceptor acceptor;
	
	private AbstractSimpHandler simpHanlder ;
	
	private SimpHandlerInterceptor handlerInterceptor;
	
	private RouterInterceptor routerInterceptor;
	
	private ProtocolCodecFilter[] codecFilters;
	
	private boolean serverIsOpen;
	
	private SimpNode node;
	
	private boolean alive = true;
	
	public SimpServerAccepter(String ip,int port,String serverName ,SimpContext context,AbstractSimpHandler simpHanlder,SimpNode node ){
		this.ip = ip;
		this.port = port;
		this.context = context;
		this.node = node;
		initContext(serverName);
		this.simpHanlder = simpHanlder;
	}
	
	public void initContext(String serverName ){
		if(context == null)
			context = new SimpContextWraper();
		context.addAttribute(Constants.NODE_NAME, serverName);
		context.addAttribute(Constants.NODE, this);
		context.setSimpNode(node);
	}
	
	public void accept() throws IOException{
		if(serverIsOpen){
			return;
		}
		this.acceptor = new NioSocketAcceptor();
		acceptor.getSessionConfig().setSoLinger(0);
		acceptor.getSessionConfig().setKeepAlive(alive);
	    acceptor.getSessionConfig().setBothIdleTime(60000);

		    if (codecFilters != null) {
		      for (int i = 0; i < codecFilters.length; i++) {
		        acceptor.getFilterChain().addLast("customCodec" + i, codecFilters[i]);
		      }
		    }
		    acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new SimpMessageCodecFactory()));
		    
		    ExecutorFilter ef = new ExecutorFilter(new ThreadPoolExecutor(4, 4, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()));
		    acceptor.getFilterChain().addLast("threadPool", ef);
		    acceptor.setHandler(simpHanlder);
		    simpHanlder.init(context, routerInterceptor, handlerInterceptor);
		    acceptor.bind(getAddress());
		    serverIsOpen = true;
		    
	}
	public void accept(AbstractSimpHandler hanlder,ProtocolCodecFilter[] codecFilters,SimpHandlerInterceptor handlerInterceptor, RouterInterceptor routerInterceptor) throws IOException{

		this.simpHanlder = hanlder;
		this.handlerInterceptor = handlerInterceptor;
		this.routerInterceptor = routerInterceptor;
		this.codecFilters = codecFilters;
		accept();
		
		    
	}
	public SocketAddress getAddress(){
		SocketAddress address = new InetSocketAddress(ip,port) ;
		return address;
	}
	
	public void close(){
		acceptor.dispose(true);
		serverIsOpen = false;
	}

	public AbstractSimpHandler getSimpHanlder() {
		return simpHanlder;
	}

	public void setSimpHanlder(AbstractSimpHandler simpHanlder) {
		this.simpHanlder = simpHanlder;
	}

	public SimpHandlerInterceptor getHandlerInterceptor() {
		return handlerInterceptor;
	}

	public void setHandlerInterceptor(SimpHandlerInterceptor handlerInterceptor) {
		this.handlerInterceptor = handlerInterceptor;
	}

	public RouterInterceptor getRouterInterceptor() {
		return routerInterceptor;
	}

	public void setRouterInterceptor(RouterInterceptor routerInterceptor) {
		this.routerInterceptor = routerInterceptor;
	}

	public ProtocolCodecFilter[] getCodecFilters() {
		return codecFilters;
	}

	public void setCodecFilters(ProtocolCodecFilter[] codecFilters) {
		this.codecFilters = codecFilters;
	}
	
	
	public SimpNode getNode() {
		return node;
	}

	public void setNode(SimpNode node) {
		this.node = node;
	}

	public void send(SimpMessage message){
		simpHanlder.send(message);
	}
	
}
